﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.IO;
using System.Configuration;
using BOOL = System.Boolean;
using DWORD = System.UInt32;
using LPWSTR = System.String;
using NET_API_STATUS = System.UInt32;


namespace SmartPhone.Un.Pro.FileManagement
{
    public class FileUtility 
    {
       
        public const string UploadLocationKey = "UploadLocation";
        public const string HRDataLocationKey = "HR_DataFolder";
        
        FileManagementDBEntities entities;

        public FileUtility()
        {
            entities = new FileManagementDBEntities();
        }


        public string GetLocation(string key)
        {
            string storeDirectory = ConfigurationManager.AppSettings[key].ToString();

            if (storeDirectory.Contains("~"))
            {
                storeDirectory = System.Web.HttpContext.Current.Server.MapPath(storeDirectory);
            }

            return storeDirectory;
        }

        #region Impersonate to access remote file

      

     

        #endregion

        #region Privates

        private string GetFileSize(decimal sizeInByte)
        {
            if (sizeInByte >= 1024)
            {
                if (sizeInByte >= 1024 * 1024)
                {
                    return (sizeInByte / (1024 * 1024)).ToString("#,##0.##") + " MB";
                }
                return (sizeInByte / 1024).ToString("#,##0.##") + " KB";
            }
            return sizeInByte.ToString("#,##0.##") + " Bytes";
        }

        private string GetFilePath(Guid FileID)
        {
            var file = entities.FileList.FirstOrDefault(p => p.FileID == FileID);
            if (file != null)
            {
                return file.PhysicalPath;
            }
            return string.Empty;
        }

        /// <summary>
        /// Make file folder path only
        /// </summary>
        /// <param name="GroupID"></param>
        /// <returns></returns>
        private string MakeFilePath(int GroupID)
        {
            string parentFolder = GetLocation(UploadLocationKey);
            if (parentFolder.Contains("~"))
            {
                parentFolder = System.Web.HttpContext.Current.Server.MapPath(parentFolder);
            }

            string path = parentFolder + "\\" + DateTime.Today.ToString("yyyyMM") + "\\Group_" + GroupID.ToString() + "\\";

            try
            {
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }

            }
            catch (Exception ex)
            {
                LogUtility.LogMessage(ex.Message);
            }

            return path;
        }

        /// <summary>
        /// make file full path, create folder if not exists
        /// </summary>
        /// <param name="GroupID"></param>
        /// <param name="fileNameNoExt"></param>
        /// <param name="fileExt"></param>
        /// <returns></returns>
        private string MakeFilePath(int GroupID, Guid FileID, string fileNameNoExt, string fileExt)
        {
            string path = MakeFilePath(GroupID);
            string fileName = FileID.ToString() + "_" + fileNameNoExt + fileExt;

            try
            {
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }

            }
            catch (Exception ex)
            {
                LogUtility.LogMessage(ex.Message);
            }


            return Path.Combine(path, fileName);
        }

        public byte[] GetFileStream(string filePath)
        {
            byte[] buffer;
            FileStream fileStream;
            try
            {
                fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            }
            catch (FileNotFoundException ex)
            {
                return null;
            }
            try
            {
                int length = (int)fileStream.Length;
                buffer = new byte[length];
                int count;
                int sum = 0;

                while ((count = fileStream.Read(buffer, sum, length - sum)) > 0)
                {
                    sum += count;
                }
            }
            finally
            {
                fileStream.Close();
            }
            return buffer;
        }

        public string RemoveGuidString(string fileName)
        {
            try
            {
                fileName = fileName.Substring(Guid.Empty.ToString().Length + 1);
            }
            catch
            {
            }
            return fileName;
        }

        /// <summary>
        /// Get or create a new group 
        /// </summary>
        /// <param name="GroupID"></param>
        /// <param name="groupName"></param>
        /// <returns></returns>
        private FileGroup  GetOrCreateGroup(int? GroupID, string groupName)
        {
            var group = entities.FileGroup.FirstOrDefault(p => p.GroupID == GroupID && GroupID != 0);
            if (group == null)
            {
                group = new FileGroup()
                {
                    Name = groupName,
                    Description = string.Empty
                };

                entities.AddToFileGroup(group);
                entities.SaveChanges();
                entities.Refresh(System.Data.Objects.RefreshMode.ClientWins, group);
            }

            return group;
        }

        private FileGroup GetOrCreateGroup(int? GroupID, string groupName, string groupDesc)
        {
            var group = entities.FileGroup.FirstOrDefault(p => p.GroupID == GroupID && GroupID != 0);
            if (group == null)
            {
                group = new FileGroup()
                {
                    Name = groupName,
                    Description = groupDesc
                };

                entities.AddToFileGroup(group);
                entities.SaveChanges();
                entities.Refresh(System.Data.Objects.RefreshMode.ClientWins, group);
            }

            return group;
        }

        /// <summary>
        /// Get files of a group
        /// </summary>
        /// <param name="GroupID"></param>
        /// <returns></returns>
        private FileList[] GetFilesByGroup(int GroupID)
        {
            return entities.FileList.Where(p => p.GroupID == GroupID).OrderBy(p => p.UpOrder).ToArray();
        }

        #endregion

        #region Public

        /// <summary>
        /// Save uploaded file into group
        /// </summary>
        /// <param name="file"></param>
        /// <param name="GroupID"></param>
        /// <returns></returns>
        public Guid SaveUploadedFile(HttpPostedFileBase file, int GroupID)
        {
            if (file != null)
            {
                if (file.ContentLength > 0)
                {
                    string name = Path.GetFileNameWithoutExtension(file.FileName);
                    string ext = Path.GetExtension(file.FileName);
                    Guid FileID = Guid.NewGuid();
                    string fileName = MakeFilePath(GroupID, FileID, name, ext);
                    file.SaveAs(fileName);

                    var group = GetOrCreateGroup(GroupID, string.Empty);

                    int? maxOrder = entities.FileList.Where(p => p.GroupID == group.GroupID).Max(p => p.UpOrder);
                    maxOrder = maxOrder ?? 0;

                    var fileItem = new FileList();
                    fileItem.FileID = FileID;
                    fileItem.UpOrder = maxOrder + 1;
                    fileItem.Name = name;
                    fileItem.Ext = ext;
                    fileItem.Size = file.ContentLength;
                    fileItem.PhysicalPath = fileName;
                    fileItem.GroupID = group.GroupID;

                    entities.AddToFileList(fileItem);
                    entities.SaveChanges();

                    return FileID;
                }
            }
            return Guid.Empty;
        }

       
        /// <summary>
        /// Move file to tmp folder to target folder and rename file name
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="GroupID"></param>
        /// <param name="tmpFolder"></param>
        /// <param name="newGroupID"></param>
        /// <returns></returns>
        public Guid SaveUploadedFile(string filePath, int GroupID, out int newGroupID)
        {
            newGroupID = GroupID;
            
            if (File.Exists(filePath))
            {
                var group = GetOrCreateGroup(GroupID, string.Empty);
                newGroupID = group.GroupID;

                Guid FileID = Guid.NewGuid();
                string name = Path.GetFileNameWithoutExtension(filePath);
                name = RemoveGuidString(name);

                string ext = Path.GetExtension(filePath);
                string targetFilePath = MakeFilePath(newGroupID, FileID, name, ext);
                                
                // move to target
                File.Move(filePath, targetFilePath);
                FileInfo fi = new FileInfo(targetFilePath);

                int? maxOrder = entities.FileList.Where(p => p.GroupID == group.GroupID).Max(p => p.UpOrder);
                maxOrder = maxOrder ?? 0;

                var fileItem = new FileList();
                fileItem.FileID = FileID;
                fileItem.UpOrder = maxOrder + 1;
                fileItem.Name = name;
                fileItem.Ext = ext;
                fileItem.Size = fi.Length;
                fileItem.PhysicalPath = targetFilePath;
                fileItem.GroupID = group.GroupID;

                entities.AddToFileList(fileItem);
                entities.SaveChanges();

                return FileID;
            }
            return Guid.Empty;
        }

        /// <summary>
        /// Move file to tmp folder to target folder and rename file name
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="GroupID"></param>
        /// <param name="tmpFolder"></param>
        /// <param name="newGroupID"></param>
        /// <returns></returns>
        public int CopyUploadedFile(string filePath, int GroupID, out int newGroupID)
        {
            newGroupID = GroupID;

            if (File.Exists(filePath))
            {
                var group = GetOrCreateGroup(GroupID, string.Empty);
                newGroupID = group.GroupID;

                Guid FileID = Guid.NewGuid();
                string name = Path.GetFileNameWithoutExtension(filePath);
                name = RemoveGuidString(name);

                string ext = Path.GetExtension(filePath);
                string targetFilePath = MakeFilePath(newGroupID, FileID, name, ext);

                // move to target
                File.Copy(filePath, targetFilePath);
                FileInfo fi = new FileInfo(targetFilePath);

                int? maxOrder = entities.FileList.Where(p => p.GroupID == group.GroupID).Max(p => p.UpOrder);
                maxOrder = maxOrder ?? 0;

                var fileItem = new FileList();
                fileItem.FileID = FileID;
                fileItem.UpOrder = maxOrder + 1;
                fileItem.Name = name;
                fileItem.Ext = ext;
                fileItem.Size = fi.Length;
                fileItem.PhysicalPath = targetFilePath;
                fileItem.GroupID = group.GroupID;

                entities.AddToFileList(fileItem);
                entities.SaveChanges();

                return newGroupID;
            }
            return 0;
        }

        /// <summary>
        /// Save a new file, and return a group id
        /// 
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="GroupID"></param>
        /// <param name="groupName"></param>
        /// <param name="groupDesc"></param>
        /// <param name="newGroupID"></param>
        /// <returns></returns>
        public Guid SaveUploadedFile(string filePath, int GroupID, string groupName, string groupDesc, out int newGroupID)
        {
            newGroupID = GroupID;

            if (File.Exists(filePath))
            {
                var group = GetOrCreateGroup(GroupID, groupName, groupDesc);
                newGroupID = group.GroupID;

                Guid FileID = Guid.NewGuid();
                string name = Path.GetFileNameWithoutExtension(filePath);
                name = RemoveGuidString(name);

                string ext = Path.GetExtension(filePath);
                string targetFilePath = MakeFilePath(newGroupID, FileID, name, ext);

                // move to target
                File.Move(filePath, targetFilePath);
                FileInfo fi = new FileInfo(targetFilePath);

                int? maxOrder = entities.FileList.Where(p => p.GroupID == group.GroupID).Max(p => p.UpOrder);
                maxOrder = maxOrder ?? 0;

                var fileItem = new FileList();
                fileItem.FileID = FileID;
                fileItem.UpOrder = maxOrder + 1;
                fileItem.Name = name;
                fileItem.Ext = ext;
                fileItem.Size = fi.Length;
                fileItem.PhysicalPath = targetFilePath;
                fileItem.GroupID = group.GroupID;

                entities.AddToFileList(fileItem);
                entities.SaveChanges();

                return FileID;
            }
            return Guid.Empty;
        }

       
      

        /// <summary>
        /// Download remote file
        /// </summary>
        /// <param name="remoteFilePath"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public bool DownloadFile(string remoteFilePath, out byte[] data)
        {
            // return true, if exists..otherwise false            
            data = GetFileStream(remoteFilePath);
            return data != null;
        }

        /// <summary>
        /// Return file stream
        /// </summary>
        /// <param name="FileID"></param>
        /// <returns></returns>
        public Stream GetFileStream(Guid FileID, out string contentType)
        {
            FileStream fs = null;
            contentType = "application/octet-stream";
            var file = GetFileInfo(FileID);
            if (file != null)
            {
                try
                {
                    contentType = "application/" + Path.GetExtension(file.PhysicalPath).Replace(".", "");
                    fs = File.OpenRead(file.PhysicalPath);
                }
                catch
                {
                }
            }

            return fs;
        }

        /// <summary>
        /// Get file list by id
        /// </summary>
        /// <param name="FileIDs"></param>
        /// <returns></returns>
        public List<string> GetFileList(Guid[] FileIDs)
        {
            List<string> fileList = new List<string>();
            for (int i = 0; i < FileIDs.Length; i++)
            {
                string fileName = GetFilePath(FileIDs[i]);
                if (fileName != string.Empty)
                {
                    fileList.Add(fileName);
                }
            }
            return (fileList.Count > 0) ? fileList : null;
        }

        /// <summary>
        /// Get file list from group
        /// </summary>
        /// <param name="GroupID"></param>
        /// <returns></returns>
        public string[] GetFileList(int GroupID)
        {
            var files = GetFilesByGroup(GroupID);
            if (files != null)
            {
                string[] filePathArray = new string[files.Length];
                for (int i = 0; i < files.Length; i++)
                {
                    filePathArray[i] = files[i].PhysicalPath;
                }
                return filePathArray;
            }
            return null;
        }

        /// <summary>
        /// Delete multiple files
        /// </summary>
        /// <param name="FileIDs"></param>
        public void DeleteFile(Guid[] FileIDs)
        {
            if (FileIDs.Length == 0)
            {
                return;
            }

            string parentFolder = string.Empty;

            var firstFileID = FileIDs[0];
            var fileInfo = entities.FileList.FirstOrDefault(p => p.FileID == firstFileID);
            int GroupID = fileInfo != null ? fileInfo.GroupID : 0;
            foreach (Guid FileID in FileIDs)
            {
                var file = GetFileInfo(FileID);
                try
                {
                    parentFolder = Path.GetDirectoryName(file.PhysicalPath);
                    File.Delete(file.PhysicalPath);
                }
                catch { }

                DeleteFileInfo(FileID);               
            }

            var count = entities.FileList.Count(p => p.GroupID == GroupID);
            if (count == 0)
            {
                var group = entities.FileGroup.FirstOrDefault(p => p.GroupID == GroupID);
                if (group != null)
                {
                    entities.FileGroup.DeleteObject(group);
                    entities.SaveChanges();
                }

                try
                {
                    if (Directory.Exists(parentFolder))
                    {
                        Directory.Delete(parentFolder);
                    }
                }
                catch
                {
                }
            }
        }

        /// <summary>
        /// Delete all files in group and real files
        /// </summary>
        /// <param name="GroupID"></param>
        public void DeleteFileGroup(int GroupID)
        {
            string parentFolder = string.Empty;
            var fileInfos = entities.FileList.Where(p => p.GroupID == GroupID).ToArray();
            foreach (var fileInfo in fileInfos)
            {
                try
                {
                    parentFolder = Path.GetDirectoryName(fileInfo.PhysicalPath);
                    File.Delete(fileInfo.PhysicalPath);
                }
                catch { }

                entities.FileList.DeleteObject(fileInfo);
                entities.SaveChanges();
            }

            try
            {
                if (Directory.Exists(parentFolder))
                {
                    Directory.Delete(parentFolder);
                }
            }
            catch
            {

            }

            // delete file group
            var fileGroup = entities.FileGroup.FirstOrDefault(p => p.GroupID == GroupID);
            if (fileGroup != null)
            {
                entities.FileGroup.DeleteObject(fileGroup);
                entities.SaveChanges();
            }
        }

        /// <summary>
        /// Delete a file
        /// </summary>
        /// <param name="FileID"></param>
        public void DeleteFileInfo(Guid FileID)
        {
            var file = entities.FileList.FirstOrDefault(p => p.FileID == FileID);
            if (file != null)
            {
                entities.FileList.DeleteObject(file);
                entities.SaveChanges();
            }
        }

        public FileList GetFileInfo(Guid FileID)
        {
            return entities.FileList.FirstOrDefault(p => p.FileID == FileID);
        }

        public FileList[] GetFileListGroup(int GroupID)
        {
            return entities.FileList.Where(p => p.GroupID == GroupID).OrderBy(p => p.UpOrder).ToArray();
        }


        public int CheckFile(Guid[] FileIDs)
        {
            int flag = 0;

            int count = FileIDs.Length;
            foreach (Guid FileID in FileIDs)
            {
                var file = GetFileInfo(FileID);
                if (file == null)
                {
                    flag++;
                }
                else if (file != null && !File.Exists(file.PhysicalPath))
                {
                    flag++;
                }
            }
            if (flag == 0)
                return 0;
            if (flag == count)
                return 2;
            return 1;
        }

        /// <summary>
        /// Check if a file exists
        /// </summary>
        /// <param name="remoteFilePath"></param>
        /// <returns></returns>
        public bool FileExists(string remoteFilePath)
        {
            return File.Exists(remoteFilePath);
        }

        #endregion

        
    }

}
